﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.SqlClient;
using System.Reflection;

namespace zUfDsl
{
    public static class ExpressionExt
    {
        #region 转换LambdaExpression为SQL语句，提供给外部调用
        /// <summary>
        /// 转换LambdaExpression为SQL语句
        /// </summary>
        /// <param name="exxx"></param>
        /// <param name="paras"></param>
        /// <returns></returns>
        public static string GetSql(this Expression exxx, out List<SqlParameter> paras)
        {
            int index = 0;
            paras = new List<SqlParameter>();
            return DealExpress(exxx, ref index, ref paras);
        }
        #endregion

        #region 转换LambdaExpression
        //private int index = 0;
        //public List<SqlParameter> paras =new List<SqlParameter>();
        private static string DealExpress(Expression exp, ref int index, ref List<SqlParameter> paras)
        {

            if (exp is LambdaExpression)
            {
                LambdaExpression lexp = exp as LambdaExpression;
                var s1 = DealExpress(lexp.Body, ref index, ref paras);
                return s1;
            }
            if (exp is BinaryExpression)
            {
                var s2 = DealBinaryExpression(exp as BinaryExpression, ref index, ref paras);
                return s2;
            }
            if (exp is MemberExpression)
            {
                var s3 = DealMemberExpression(exp as MemberExpression,ref index,ref paras);
                return s3;
            }
            if (exp is ConstantExpression)
            {
                var s4 = DealConstantExpression(exp as ConstantExpression, ref index, ref paras);
                return s4;
            }
            if (exp is UnaryExpression)
            {
                var s5 = DealUnaryExpression(exp as UnaryExpression, ref index, ref paras);
                return s5;
            }
            return "";
        }
        #endregion

        #region 一元符号，暂时不支持
        private static string DealUnaryExpression(UnaryExpression exp, ref int index, ref List<SqlParameter> paras)
        {
            return DealExpress(exp.Operand, ref index, ref paras);
        }
        #endregion

        #region 值处理
        private static string DealConstantExpression(ConstantExpression exp, ref int index, ref List<SqlParameter> paras)
        {
            object vaule = exp.Value;
            string vstr = string.Empty;
            if (vaule == null)
            {
                return "NULL";
            }
            if (vaule is string)
            {
                vstr = "@p" + index.ToString();
                var para = new SqlParameter("@p" + index.ToString(), string.Format("{0}", vaule.ToString()));
                paras.Add(para);
                index++;
            }
            else if (vaule is DateTime)
            {
                DateTime time = (DateTime)vaule;
                vstr = string.Format("'{0}'", time.ToString("yyyy-MM-dd HH:mm:ss"));
            }
            else
            {
                vstr = "@p" + index.ToString();
                var para = new SqlParameter("@p" + index.ToString(), string.Format("{0}", vaule.ToString()));
                paras.Add(para);
                index++;
            }
            return vstr;
        }
        #endregion

        #region 拆分条件表达式
        private static string DealBinaryExpression(BinaryExpression exp, ref int index, ref List<SqlParameter> paras)
        {
            string left;
            if (exp.Left is BinaryExpression && (exp.Left as BinaryExpression).Left is BinaryExpression)
            { left = "(" + DealExpress(exp.Left, ref index, ref paras) + ")"; }
            else
            { left = DealExpress(exp.Left, ref index, ref paras); }
            string oper = GetOperStr(exp.NodeType);
            string right;
            if (exp.Right is BinaryExpression && (exp.Right as BinaryExpression).Left is BinaryExpression)
            {
                right = "(" + DealExpress(exp.Right, ref index, ref paras) + ")";
            }
            else { right = DealExpress(exp.Right, ref index, ref paras); }
            if (right == "NULL")
            {
                if (oper == "=")
                {
                    oper = " is ";
                }
                else
                {
                    oper = " is not ";
                }
            }
            return left + oper + right;
        }
        #endregion


        #region 条件列名
        private static string DealMemberExpression(MemberExpression exp, ref int index, ref List<SqlParameter> paras)
        {
            if (exp.Expression.NodeType == ExpressionType.Constant)
            {
                //var bb = exp.Expression as ConstantExpression;
                return DealMemberExpressionConstand(exp, exp.Member.Name,ref index,ref paras);
            }
            else
            { return exp.Member.Name; }
        }

        private static string DealMemberExpressionConstand(MemberExpression conexp,string filename, ref int index, ref List<SqlParameter> paras)
        {
            //var value = conexp as MemberExpression;
            object vv = GetMemberValue(conexp.Member, conexp);
            //var field = value.GetType().GetField(filename);
            //if (field != null)
            //{ vv = field.GetValue(value); }
            //else
            //{
            //    var pp = value.GetType().GetProperty(filename);
            //    if (pp != null) { vv = pp.GetValue(value, null); }
            //}
            string vstr;
            if (vv is string)
            {
                vstr = "@p" + index.ToString();
                var para = new SqlParameter("@p" + index.ToString(), string.Format("{0}", vv.ToString()));
                paras.Add(para);
                index++;
            }
            else if (vv is DateTime)
            {
                DateTime time = (DateTime)vv;
                vstr = string.Format("'{0}'", time.ToString("yyyy-MM-dd HH:mm:ss"));
            }
            else
            {
                vstr = "@p" + index.ToString();
                var para = new SqlParameter("@p" + index.ToString(), string.Format("{0}", vv.ToString()));
                paras.Add(para);
                index++;
            }
            return vstr;

        }



        #endregion

        #region 符号处理
        private static string GetOperStr(ExpressionType etype)
        {
            switch (etype)
            {
                case ExpressionType.OrElse: return " OR ";
                case ExpressionType.Or: return "|";
                case ExpressionType.AndAlso: return " AND ";
                case ExpressionType.And: return " AND ";
                case ExpressionType.GreaterThan: return ">";
                case ExpressionType.GreaterThanOrEqual: return ">=";
                case ExpressionType.LessThan: return "<";
                case ExpressionType.LessThanOrEqual: return "<=";
                case ExpressionType.NotEqual: return "<>";
                case ExpressionType.Add: return "+";
                case ExpressionType.Subtract: return "-";
                case ExpressionType.Multiply: return "*";
                case ExpressionType.Divide: return "/";
                case ExpressionType.Modulo: return "%";
                case ExpressionType.Equal: return "=";
            }
            return "";
        }

        private static object GetMemberValue(MemberInfo member, Expression expression)
        {
            var memberInfos = new Stack<MemberInfo>();
            var fieldInfo = member as System.Reflection.FieldInfo;
            object reval = null;
            // "descend" toward's the root object reference:
            while (expression is MemberExpression)
            {
                var memberExpr = expression as MemberExpression;
                memberInfos.Push(memberExpr.Member);
                if (memberExpr.Expression == null)
                {
                    var isProperty = memberExpr.Member.MemberType == MemberTypes.Property;
                    var isField = memberExpr.Member.MemberType == MemberTypes.Field;
                    if (isProperty)
                    {
                        reval = GetPropertyValue(memberExpr);
                    }
                    else if (isField)
                    {
                        reval = GetFiledValue(memberExpr);
                    }
                }
                if (memberExpr.Expression == null)
                {

                }
                expression = memberExpr.Expression;
            }
            // fetch the root object reference:
            var constExpr = expression as ConstantExpression;
            object objReference = constExpr.Value;
            // "ascend" back whence we came from and resolve object references along the way:
            while (memberInfos.Count > 0)  // or some other break condition
            {
                var mi = memberInfos.Pop();
                if (mi.MemberType == MemberTypes.Property)
                {
                    var objProp = objReference.GetType().GetProperty(mi.Name);
                    if (objProp == null)
                    {

                    }
                    objReference = objProp.GetValue(objReference, null);
                }
                else if (mi.MemberType == MemberTypes.Field)
                {
                    var objField = objReference.GetType().GetField(mi.Name);
                    if (objField == null)
                    {

                    }
                    objReference = objField.GetValue(objReference);
                }
            }
            reval = objReference;
            return reval;
        }

        private  static object GetFiledValue(MemberExpression memberExpr)
        {
            object reval = null;
            FieldInfo field = (FieldInfo)memberExpr.Member;
            reval = field.GetValue(memberExpr.Member);
            if (reval != null && reval.GetType().IsClass && reval.GetType() != typeof(string))
            {
                var fieldName = memberExpr.Member.Name;
                var proInfo = reval.GetType().GetProperty(fieldName);
                if (proInfo != null)
                {
                    reval = proInfo.GetValue(reval, null);
                }
                var fieInfo = reval.GetType().GetField(fieldName);
                if (fieInfo != null)
                {
                    reval = fieInfo.GetValue(reval);
                }
                if (fieInfo == null && proInfo == null)
                {

                }
            }
            return reval;
        }

        private static object GetPropertyValue(MemberExpression memberExpr)
        {
            object reval = null;
            PropertyInfo pro = (PropertyInfo)memberExpr.Member;
            reval = pro.GetValue(memberExpr.Member, null);
            if (reval != null && reval.GetType().IsClass && reval.GetType() != typeof(string))
            {
                var fieldName = memberExpr.Member.Name;
                var proInfo = reval.GetType().GetProperty(fieldName);
                if (proInfo != null)
                {
                    reval = proInfo.GetValue(reval, null);
                }
                var fieInfo = reval.GetType().GetField(fieldName);
                if (fieInfo != null)
                {
                    reval = fieInfo.GetValue(reval);
                }
                if (fieInfo == null && proInfo == null)
                {

                }
            }
            return reval;
        }

        #endregion
    }
}
